{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "naMQPO5iRpNE"
   },
   "source": [
    "#### Copyright 2018 Google LLC."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sHpbnneJYcwJ"
   },
   "outputs": [],
   "source": [
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "84x4Fxc5lzFv"
   },
   "source": [
    "# Introducción a la equidad en el AA\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "J8daw3YOIAXH"
   },
   "source": [
    "## Renuncia de responsabilidad\n",
    "Este ejercicio explora solo un pequeño subconjunto de ideas y técnicas relevantes para la equidad en el aprendizaje automático, pero el contenido no es exhaustivo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xFxZOg55lWJE"
   },
   "source": [
    "## Objetivos de aprendizaje\n",
    "\n",
    "* Aumentar el conocimiento sobre los diferentes tipos de sesgo que se pueden manifestar en los datos del modelo\n",
    "* Explorar los datos de los atributos para identificar de forma proactiva las posibles fuentes de sesgo antes de entrenar un modelo\n",
    "* Evaluar el rendimiento del modelo por subgrupo y no en conjunto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "l-K-xqksm-X3"
   },
   "source": [
    "## Descripción general\n",
    "\n",
    "Aquí explorarás conjuntos de datos y evaluarás los clasificadores teniendo en cuenta la *equidad* y los modos en que los sesgos no deseados pueden afectar el aprendizaje automático (AA).\n",
    "\n",
    "Durante todo el ejercicio verás tareas de **FairAware**, que brindan oportunidades para contextualizar los procesos de AA con respecto a la equidad. Cuando completes estas tareas, identificarás los sesgos y considerarás el impacto a largo plazo que pueden sufrir las predicciones del modelo cuando no se abordan estos sesgos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "TXkkHYyJ98_k"
   },
   "source": [
    "## Acerca del conjunto de datos y las tareas de predicción\n",
    "\n",
    "En este ejercicio, trabajarás con el [conjunto de datos del Censo de ingresos por adulto](https://archive.ics.uci.edu/ml/datasets/Census+Income), que se usa comúnmente en la bibliografía del aprendizaje automático. Estos datos fueron extraídos de la [base de datos de la Oficina del Censo de 1994](http://www.census.gov/en.html) por Ronny Kohavi y Barry Becker.\n",
    "\n",
    "Cada ejemplo del conjunto de datos incluye los siguientes datos demográficos de un grupo de individuos que participó en el Censo de 1994:\n",
    "\n",
    "### Atributos numéricos\n",
    "*   `age`: La edad de los individuos en años\n",
    "*   `fnlwgt`: La cantidad de individuos que representa este conjunto de observaciones, según las Organizaciones del Censo\n",
    "*   `education_num`: Una enumeración de la representación categórica del nivel de educación. Cuanto más alto el número, más alto el nivel de educación alcanzado por el individuo. Por ejemplo, un valor de `11` en `education_num` equivale a `Assoc_voc` (diploma universitario de dos años en una escuela vocacional), un valor de `13` equivale a `Bachelors` (licenciatura) y un valor de `9` equivale a `HS-grad` (egresado de la preparatoria)\n",
    "*   `capital_gain`: Ganancia de capital del individuo, representada en dólares estadounidenses\n",
    "*   `capital_loss`: Pérdida de capital del individuo, representada en dólares estadounidenses\n",
    "*   `hours_per_week`: Horas de trabajo por semana\n",
    "\n",
    "### Atributos categóricos\n",
    "*   `workclass`: El tipo de empleador del individuo. Algunos ejemplos son `Private`, `Self-emp-not-inc`, `Self-emp-inc`, `Federal-gov`, `Local-gov`, `State-gov`, `Without-pay` y `Never-worked`\n",
    "*   `education`: El nivel más alto de educación alcanzado por el individuo\n",
    "*   `marital_status`: El estado civil del individuo. Algunos ejemplos son `Married-civ-spouse`, `Divorced`, `Never-married`, `Separated`, `Widowed`, `Married-spouse-absent` y `Married-AF-spouse`\n",
    "*   `occupation`: La ocupación del individuo. Algunos ejemplos son `tech-support`, `Craft-repair`, `Other-service`, `Sales` y `Exec-managerial`, entre otros\n",
    "*   `relationship`: La relación de cada individuo en el hogar. Algunos ejemplos son `Wife`, `Own-child`, `Husband`, `Not-in-family`, `Other-relative` y `Unmarried`\n",
    "*   `gender`: El género del individuo, disponible únicamente en opciones binarias: `Female` o `Male`\n",
    "*   `race`: `White`, `Asian-Pac-Islander`, `Amer-Indian-Eskimo`, `Black` y `Other`\n",
    "*   `native_country`: País de origen del individuo. Algunos ejemplos son `United-States`, `Cambodia`, `England`, `Puerto-Rico`, `Canada`, `Germany`, `Outlying-US(Guam-USVI-etc)`, `India`, `Japan`, `United-States`, `Cambodia`, `England`, `Puerto-Rico`, `Canada`, `Germany`, `Outlying-US(Guam-USVI-etc)`, `India` y `Japan`, entre otros\n",
    "\n",
    "### Tarea de predicción\n",
    "La tarea de predicción consiste en **determinar si una persona gana más de $50,000 dólares al año.**\n",
    "\n",
    "### Etiqueta\n",
    "*   `income_bracket`: Si la persona gana más de $50,000 dólares al año\n",
    "\n",
    "### Notas sobre el conjunto de datos\n",
    "\n",
    "Todos los ejemplos extraídos para este conjunto de datos cumplen con las siguientes condiciones:\n",
    "*   `age` es 16 años o más.\n",
    "*   El ingreso bruto ajustado (que se usa para calcular `income_bracket`) es mayor que USD 100 al año.\n",
    "*   `fnlwgt` es mayor que 0.\n",
    "*   `hours_per_week` es mayor que 0.\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "I0RMIktKy8xX"
   },
   "source": [
    "## Configuración\n",
    "\n",
    "Primero, importa algunos módulos para usarlos en este bloc de notas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "2e_0DJJ8zE29"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import tensorflow as tf\n",
    "import tempfile\n",
    "!pip install seaborn==0.8.1\n",
    "import seaborn as sns\n",
    "import itertools\n",
    "from sklearn.metrics import confusion_matrix\n",
    "from sklearn.metrics import roc_curve, roc_auc_score\n",
    "from sklearn.metrics import precision_recall_curve\n",
    "from google.colab import widgets\n",
    "# For facets\n",
    "from IPython.core.display import display, HTML\n",
    "import base64\n",
    "!pip install facets-overview==1.0.0\n",
    "from facets_overview.feature_statistics_generator import FeatureStatisticsGenerator\n",
    "\n",
    "print('Modules are imported.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "-xgIRapb5LaQ"
   },
   "source": [
    "### Carga del conjunto de datos Adulto\n",
    "\n",
    "Con los módulos importados, podemos cargar el conjunto de datos Adulto en la estructura de datos DataFrame de Pandas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "TeCNVvVUVS0P"
   },
   "outputs": [],
   "source": [
    "COLUMNS = [\"age\", \"workclass\", \"fnlwgt\", \"education\", \"education_num\",\n",
    "           \"marital_status\", \"occupation\", \"relationship\", \"race\", \"gender\",\n",
    "           \"capital_gain\", \"capital_loss\", \"hours_per_week\", \"native_country\",\n",
    "           \"income_bracket\"]\n",
    "\n",
    "train_df = pd.read_csv(\n",
    "    \"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data\",\n",
    "    names=COLUMNS,\n",
    "    sep=r'\\s*,\\s*',\n",
    "    engine='python',\n",
    "    na_values=\"?\")\n",
    "test_df = pd.read_csv(\n",
    "    \"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test\",\n",
    "    names=COLUMNS,\n",
    "    sep=r'\\s*,\\s*',\n",
    "    skiprows=[0],\n",
    "    engine='python',\n",
    "    na_values=\"?\")\n",
    "\n",
    "# Drop rows with missing values\n",
    "train_df = train_df.dropna(how=\"any\", axis=0)\n",
    "test_df = test_df.dropna(how=\"any\", axis=0)\n",
    "\n",
    "print('UCI Adult Census Income dataset loaded.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "coilRN-hooja"
   },
   "source": [
    "## Análisis del conjunto de datos Adulto con Facets\n",
    "\n",
    "Como se dijo en el Curso intensivo de aprendizaje automático, es importante entender el conjunto de datos *antes* de emprender la tarea de predicción. \n",
    "\n",
    "Estas son algunas preguntas importantes que se deben investigar cuando se audita un conjunto de datos para ver si hay equidad:\n",
    "\n",
    "* **¿Faltan valores de atributos para una gran cantidad de observaciones?**\n",
    "* **¿Faltan atributos que podrían afectar otros atributos?**\n",
    "* **¿Hay valores inesperados de los atributos?**\n",
    "* **¿Qué señales de distorsión de datos puedes observar?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9yCIuAqWA1Pm"
   },
   "source": [
    "Para empezar, podemos usar [Facets Overview](https://pair-code.github.io/facets/), una herramienta de visualización interactiva que puede ayudarnos a explorar el conjunto de datos. Con Facets Overview, podemos analizar rápidamente la distribución de los valores a lo largo del conjunto de datos Adulto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "MW-qryqs1gig"
   },
   "outputs": [],
   "source": [
    "#@title Visualize the Data in Facets\n",
    "fsg = FeatureStatisticsGenerator()\n",
    "dataframes = [\n",
    "    {'table': train_df, 'name': 'trainData'}]\n",
    "censusProto = fsg.ProtoFromDataFrames(dataframes)\n",
    "protostr = base64.b64encode(censusProto.SerializeToString()).decode(\"utf-8\")\n",
    "\n",
    "\n",
    "HTML_TEMPLATE = \"\"\"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.3.3/webcomponents-lite.js\"></script>\n",
    "        <link rel=\"import\" href=\"https://raw.githubusercontent.com/PAIR-code/facets/1.0.0/facets-dist/facets-jupyter.html\">\n",
    "        <facets-overview id=\"elem\"></facets-overview>\n",
    "        <script>\n",
    "          document.querySelector(\"#elem\").protoInput = \"{protostr}\";\n",
    "        </script>\"\"\"\n",
    "html = HTML_TEMPLATE.format(protostr=protostr)\n",
    "display(HTML(html))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "91wjnZFpPWw-"
   },
   "source": [
    "### Tarea 1 de FairAware\n",
    "\n",
    "Revisa las estadísticas descriptivas y los histogramas de cada atributo numérico y continuo. Para ver la distribución de los valores por categoría, haz clic en el botón **Show Raw Data** (Mostrar datos sin procesar) sobre los histogramas de atributos categóricos.\n",
    "\n",
    "Luego, intenta responder las preguntas anteriores:\n",
    "\n",
    "1. ¿Faltan valores de atributos para una gran cantidad de observaciones?\n",
    "2. ¿Faltan atributos que podrían afectar otros atributos?\n",
    "3. ¿Hay valores inesperados de los atributos?\n",
    "4. ¿Qué señales de distorsión de datos puedes observar?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "KlF-lQ8yQ69b"
   },
   "source": [
    "### Solución\n",
    "\n",
    "Para ver la información que descubrimos, haz clic a continuación."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xX_qjj5AQ_Hb"
   },
   "source": [
    "Tras revisar las columnas **faltantes** de los atributos tanto numéricos como categóricos, podemos observar que no faltan valores de atributos; por lo tanto, no es una preocupación en este caso. \n",
    "\n",
    "Cuando vemos los valores mín. y máx. y los histogramas de cada atributo numérico, podemos identificar cualquier valor atípico extremo en nuestro conjunto de datos. Para el atributo `hours_per_week`, podemos ver que el mínimo es 1; eso podría ser una sorpresa si se tiene en cuenta que, por lo general, la mayoría de los empleos requiere varias horas de trabajo por semana. Para los atributos `capital_gain` y `capital_loss`, vemos que más del 90% de los valores es 0. Dado que las ganancias/pérdidas de capital solo las registran los individuos que hacen inversiones, sin duda es posible que menos del 10% de los ejemplos tengan valores distintos de cero en esos atributos, pero tenemos que observar con más atención para verificar que los valores de esos atributos sean válidos.\n",
    "\n",
    "Cuando miramos el histograma del género, vemos que más de dos tercios (aproximadamente el 67%) de los ejemplos representan a los hombres. Esto sugiere claramente que existe una distorsión de datos, porque se esperaría que la distribución de datos entre los géneros se acercara al 50/50."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hKj2hz-Sql7V"
   },
   "source": [
    "### Análisis exhaustivo\n",
    "\n",
    "Para explorar mejor el conjunto de datos, podemos usar [Facets Dive](https://pair-code.github.io/facets/), una herramienta con interfaz interactiva en la que cada elemento individual de la visualización representa un punto de datos. Pero para usar Facets Dive, tenemos que convertir nuestros datos a una matriz JSON.\n",
    "Afortunadamente, el método DataFrame `to_json()` se encarga de esto por nosotros.\n",
    "\n",
    "Para transformar los datos a JSON y cargar Facets Dive, ejecuta la celda a continuación. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "stlklrG_xssF"
   },
   "outputs": [],
   "source": [
    "#@title Set the Number of Data Points to Visualize in Facets Dive\n",
    "\n",
    "SAMPLE_SIZE = 2500 #@param\n",
    "  \n",
    "train_dive = train_df.sample(SAMPLE_SIZE).to_json(orient='records')\n",
    "\n",
    "HTML_TEMPLATE = \"\"\"<script src=\"https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.3.3/webcomponents-lite.js\"></script>\n",
    "        <link rel=\"import\" href=\"https://raw.githubusercontent.com/PAIR-code/facets/1.0.0/facets-dist/facets-jupyter.html\">\n",
    "        <facets-dive id=\"elem\" height=\"600\"></facets-dive>\n",
    "        <script>\n",
    "          var data = {jsonstr};\n",
    "          document.querySelector(\"#elem\").data = data;\n",
    "        </script>\"\"\"\n",
    "html = HTML_TEMPLATE.format(jsonstr=train_dive)\n",
    "display(HTML(html))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "LxqAPDcRDFB2"
   },
   "source": [
    "## Tarea 2 de FairAware\n",
    "\n",
    "Usa los menús del panel de visualización de la izquierda para cambiar la organización de los datos:\n",
    "\n",
    "1. En el menú **Faceting | X-Axis**, selecciona **education** y, luego, en los menús **Display | Color** y **Display | Type**, selecciona **income_bracket**. ¿Cómo describirías la relación entre el nivel de educación y la categoría de ingresos?\n",
    "\n",
    "2. Luego, en el menú **Faceting | X-Axis**, selecciona **marital_status** y, en los menús **Display | Color** y **Display | Type**, selecciona **gender**. ¿Qué observación notable puedes hacer sobre la distribución de género en cada categoría marital-status?\n",
    "\n",
    "A medida que completes estas tareas, recuerda las siguientes preguntas sobre equidad:\n",
    "\n",
    "* **¿Qué falta?**\n",
    "* **¿Qué está sobregeneralizado?**\n",
    "* **¿Qué está subrepresentado?**\n",
    "* **¿Cómo reflejan las variables y sus valores el mundo real?**\n",
    "* **¿Qué podríamos estar ignorando?**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qZ-9vJgSEpHj"
   },
   "source": [
    "### Solución\n",
    "\n",
    "Para ver la información que descubrimos, haz clic a continuación."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "uYpbgdATEx8L"
   },
   "source": [
    "1. Por lo general, en nuestro conjunto de datos, el nivel de educación superior tiende a correlacionarse con la categoría de ingresos más altos. Un nivel de ingresos superior a USD 50,000 está más representado en los ejemplos en los que el nivel de educación es de licenciatura o superior.\n",
    "\n",
    "2. En la mayoría de las categorías de estado civil, la distribución entre hombres y mujeres se acerca a 1:1. La excepción notable es el caso de \"married-civ-spouse\", en el que los hombres superan a las mujeres en más de 5:1. Si se tiene en cuenta que en la Tarea 1 ya descubrimos que hay una representación excesiva de hombres en nuestro conjunto de datos, ahora podemos inferir que específicamente las mujeres casadas son las que están subrepresentadas en nuestros datos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "7YVH8hYfSjer"
   },
   "source": [
    "### Resumen\n",
    "\n",
    "Representar los histogramas, clasificar los ejemplos comunes de mayor a menor, identificar ejemplos duplicados o faltantes, garantizar que los conjuntos de entrenamiento y pruebas sean similares y calcular los cuantiles de los atributos **son tareas fundamentales para el análisis de los datos**.\n",
    "\n",
    "**Mientras mejor sepas lo que sucede con tus datos, más información tendrás para identificar casos de falta de equidad.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2ivWw9Wpj67m"
   },
   "source": [
    "### Tarea 3 de FairAware\n",
    "\n",
    "Ahora que exploraste el conjunto de datos con Facets, averigua si puedes identificar algunos de los problemas que podrían surgir con la equidad a partir de lo que aprendiste sobre sus atributos.\n",
    "\n",
    "¿Cuál de los siguientes atributos representa un problema en cuanto a la equidad?\n",
    "\n",
    "Selecciona un atributo de la lista desplegable de opciones en la celda a continuación y ejecuta la celda para verificar tu respuesta. Luego, explora las demás opciones para obtener más información sobre cómo influye cada una en los modelos de predicciones."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "8bFDVCV1sxiX"
   },
   "outputs": [],
   "source": [
    "feature = 'capital_gain / capital_loss' #@param [\"\", \"hours_per_week\", \"fnlwgt\", \"gender\", \"capital_gain / capital_loss\", \"age\"] {allow-input: false}\n",
    "\n",
    "\n",
    "if feature == \"hours_per_week\":\n",
    "  print(\n",
    "'''It does seem a little strange to see 'hours_per_week' max out at 99 hours,\n",
    "which could lead to data misrepresentation. One way to address this is by\n",
    "representing 'hours_per_week' as a binary \"working 40 hours/not working 40\n",
    "hours\" feature. Also keep in mind that data was extracted based on work hours\n",
    "being greater than 0. In other words, this feature representation exclude a\n",
    "subpopulation of the US that is not working. This could skew the outcomes of the\n",
    "model.''')\n",
    "if feature == \"fnlwgt\":\n",
    "  print(\n",
    "\"\"\"'fnlwgt' represents the weight of the observations. After fitting the model\n",
    "to this data set, if certain group of individuals end up performing poorly \n",
    "compared to other groups, then we could explore ways of reweighting each data \n",
    "point using this feature.\"\"\")\n",
    "if feature == \"gender\":\n",
    "  print(\n",
    "\"\"\"Looking at the ratio between men and women shows how disproportionate the data\n",
    "is compared to the real world where the ratio (at least in the US) is closer to\n",
    "1:1. This could pose a huge probem in performance across gender. Considerable\n",
    "measures may need to be taken to upsample the underrepresented group (in this\n",
    "case, women).\"\"\")\n",
    "if feature == \"capital_gain / capital_loss\":\n",
    "  print(\n",
    "\"\"\"Both 'capital_gain' and 'capital_loss' have very low variance, which might\n",
    "suggest they don't contribute a whole lot of information for predicting income. It\n",
    "may be okay to omit these features rather than giving the model more noise.\"\"\")\n",
    "if feature == \"age\":\n",
    "  print(\n",
    "'''\"age\" has a lot of variance, so it might benefit from bucketing to learn\n",
    "fine-grained correlations between income and age, as well as to prevent\n",
    "overfitting.''')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "n3OT-YVpftEI"
   },
   "source": [
    "## Predicciones con estimadores de TensorFlow\n",
    "\n",
    "Ahora que tenemos una mejor idea del conjunto de datos Adulto, podemos empezar a crear una red neuronal para predecir los ingresos. En esta sección, usaremos la API de Estimator de TensorFlow para acceder a la clase `DNNClassifier`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ECBRATBVG4rn"
   },
   "source": [
    "### Conversión del conjunto de datos Adulto en tensores\n",
    "Primero, debemos definir nuestra función de entrada, que tomará el conjunto de datos Adulto del DataFrame de Pandas y lo convertirá en tensores con la función ```tf.estimator.inputs.pandas_input_fn()```."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Bt-rQvJLx4Hm"
   },
   "outputs": [],
   "source": [
    "def csv_to_pandas_input_fn(data, batch_size=100, num_epochs=1, shuffle=False):\n",
    "  return tf.estimator.inputs.pandas_input_fn(\n",
    "      x=data.drop('income_bracket', axis=1),\n",
    "      y=data['income_bracket'].apply(lambda x: \">50K\" in x).astype(int),\n",
    "      batch_size=batch_size,\n",
    "      num_epochs=num_epochs,\n",
    "      shuffle=shuffle,\n",
    "      num_threads=1)\n",
    "\n",
    "print('csv_to_pandas_input_fn() defined.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "0mz2sts6IjBO"
   },
   "source": [
    "### Representación de los atributos en TensorFlow\n",
    "TensorFlow necesita que los datos se representen en un modelo. Para lograr esto, debes usar ```tf.feature_columns``` para ingresar y representar los atributos en TensorFlow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "tAG5hUJwx725"
   },
   "outputs": [],
   "source": [
    "#@title Categorical Feature Columns\n",
    "\n",
    "# Since we don't know the full range of possible values with occupation and\n",
    "# native_country, we'll use categorical_column_with_hash_bucket() to help map\n",
    "# each feature string into an integer ID.\n",
    "occupation = tf.feature_column.categorical_column_with_hash_bucket(\n",
    "    \"occupation\", hash_bucket_size=1000)\n",
    "native_country = tf.feature_column.categorical_column_with_hash_bucket(\n",
    "    \"native_country\", hash_bucket_size=1000)\n",
    "\n",
    "# For the remaining categorical features, since we know what the possible values\n",
    "# are, we can be more explicit and use categorical_column_with_vocabulary_list()\n",
    "gender = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"gender\", [\"Female\", \"Male\"])\n",
    "race = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"race\", [\n",
    "        \"White\", \"Asian-Pac-Islander\", \"Amer-Indian-Eskimo\", \"Other\", \"Black\"\n",
    "    ])\n",
    "education = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"education\", [\n",
    "        \"Bachelors\", \"HS-grad\", \"11th\", \"Masters\", \"9th\",\n",
    "        \"Some-college\", \"Assoc-acdm\", \"Assoc-voc\", \"7th-8th\",\n",
    "        \"Doctorate\", \"Prof-school\", \"5th-6th\", \"10th\", \"1st-4th\",\n",
    "        \"Preschool\", \"12th\"\n",
    "    ])\n",
    "marital_status = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"marital_status\", [\n",
    "        \"Married-civ-spouse\", \"Divorced\", \"Married-spouse-absent\",\n",
    "        \"Never-married\", \"Separated\", \"Married-AF-spouse\", \"Widowed\"\n",
    "    ])\n",
    "relationship = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"relationship\", [\n",
    "        \"Husband\", \"Not-in-family\", \"Wife\", \"Own-child\", \"Unmarried\",\n",
    "        \"Other-relative\"\n",
    "    ])\n",
    "workclass = tf.feature_column.categorical_column_with_vocabulary_list(\n",
    "    \"workclass\", [\n",
    "        \"Self-emp-not-inc\", \"Private\", \"State-gov\", \"Federal-gov\",\n",
    "        \"Local-gov\", \"?\", \"Self-emp-inc\", \"Without-pay\", \"Never-worked\"\n",
    "    ])\n",
    "\n",
    "print('Categorical feature columns defined.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "Jwtuu8MmyKCJ"
   },
   "outputs": [],
   "source": [
    "#@title Numeric Feature Columns\n",
    "# For Numeric features, we can just call on feature_column.numeric_column()\n",
    "# to use its raw value instead of having to create a map between value and ID.\n",
    "age = tf.feature_column.numeric_column(\"age\")\n",
    "fnlwgt = tf.feature_column.numeric_column(\"fnlwgt\")\n",
    "education_num = tf.feature_column.numeric_column(\"education_num\")\n",
    "capital_gain = tf.feature_column.numeric_column(\"capital_gain\")\n",
    "capital_loss = tf.feature_column.numeric_column(\"capital_loss\")\n",
    "hours_per_week = tf.feature_column.numeric_column(\"hours_per_week\")\n",
    "\n",
    "print('Numeric feature columns defined.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "3WqAbug6jePb"
   },
   "source": [
    "#### Creación de un atributo categórico de edad\n",
    "\n",
    "Si seleccionaste `age` cuando completaste la **Tarea 3 de FairAware**, notaste que sugerimos que el atributo `age` podría beneficiarse del *agrupamiento* (también conocido como *discretización*), es decir, de agrupar edades similares en grupos diferentes. Esto podría ayudar al modelo a generalizar mejor la edad. Entonces, convertimos el atributo numérico `age` (técnicamente, un [atributo ordinal](https://en.wikipedia.org/wiki/Ordinal_data)) en un atributo categórico."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "HxVm8X15yLR7"
   },
   "outputs": [],
   "source": [
    "age_buckets = tf.feature_column.bucketized_column(\n",
    "    age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2lx4JuLdi7jw"
   },
   "source": [
    "#### Consideración de los subgrupos clave\n",
    "\n",
    "Cuando se realiza la ingeniería de atributos, es importante recordar que esta podría trabajar con datos extraídos de individuos pertenecientes a subgrupos. En tal caso, es posible que quieras evaluar el rendimiento del modelo de forma independiente.\n",
    "\n",
    "**_NOTA:_** *En este contexto, un subgrupo se define como un grupo de individuos que comparten una característica determinada, como la raza, el género o la orientación sexual, que amerita una consideración especial cuando se evalúa un modelo teniendo en cuenta la equidad.*\n",
    "\n",
    "Cuando queremos que nuestros modelos mitiguen o aprovechen las señales aprendidas de una característica correspondiente a un subgrupo, usaremos diferentes tipos de herramientas y técnicas; **en este punto, la mayoría siguen siendo investigaciones abiertas**.\n",
    "\n",
    "Mientras trabajas con diferentes variables y defines tareas para ellas, podría ser útil pensar en lo que sigue. Por ejemplo, *¿en qué puntos la interacción entre la variable y la tarea podría ser una preocupación?*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5aD1OM8egad9"
   },
   "source": [
    "### Definición de los atributos del modelo\n",
    "\n",
    "Ahora podemos definir explícitamente los atributos que incluiremos en nuestro modelo.\n",
    "\n",
    "Consideraremos `gender` como un subgrupo y lo guardaremos en una lista de `subgroup_variables` separada a fin de poder agregarle manejos especiales si hiciera falta."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "O68xV_24gbnD"
   },
   "outputs": [],
   "source": [
    "# List of variables, with special handling for gender subgroup.\n",
    "variables = [native_country, education, occupation, workclass,\n",
    "             relationship, age_buckets]\n",
    "subgroup_variables = [gender]\n",
    "feature_columns = variables + subgroup_variables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "3nYSMg67jWaA"
   },
   "source": [
    "### Entrenamiento de un modelo de redes neuronales profundas en el conjunto de datos Adulto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "_kRL5rScH1F7"
   },
   "source": [
    "Ahora que los atributos están listos, podemos intentar predecir los ingresos con el aprendizaje profundo.\n",
    "\n",
    "Para simplificar, vamos a **definir simplemente una red neuronal de redirección de feed con dos capas ocultas** para mantener liviana la arquitectura de la red neuronal.\n",
    "\n",
    "Sin embargo, antes debemos convertir nuestros atributos categóricos altamente dimensionales en un vector denso con baja dimensión y valores reales al que llamamos vector de incorporación. Los valores de ```indicator_column``` (que se puede considerar como una codificación de un solo 1) y ```embedding_column``` (que convierte los atributos dispersos en densos) nos ayudan a agilizar el proceso.\n",
    "\n",
    "Las siguientes celdas crean las columnas profundas que se necesitan para avanzar en la definición del modelo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "code",
    "colab": {},
    "colab_type": "code",
    "id": "bnyw4cyLTSUB"
   },
   "outputs": [],
   "source": [
    "deep_columns = [\n",
    "    tf.feature_column.indicator_column(workclass),\n",
    "    tf.feature_column.indicator_column(education),\n",
    "    tf.feature_column.indicator_column(age_buckets),\n",
    "    tf.feature_column.indicator_column(gender),\n",
    "    tf.feature_column.indicator_column(relationship),\n",
    "    tf.feature_column.embedding_column(native_country, dimension=8),\n",
    "    tf.feature_column.embedding_column(occupation, dimension=8),\n",
    "]\n",
    "\n",
    "print(deep_columns)\n",
    "print('Deep columns created.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lBaCn_Z1PshC"
   },
   "source": [
    "Después de haber realizado el procesamiento previo de todos nuestros datos, ahora podemos definir el modelo de nuestra red neuronal profunda. Comencemos con los parámetros que se definen a continuación. (Después de definir las métricas de evaluación y evaluar el modelo, puedes regresar y ajustar los parámetros para comparar los resultados).\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "tQZ1kumWk8XO"
   },
   "outputs": [],
   "source": [
    "#@title Define Deep Neural Net Model\n",
    "\n",
    "HIDDEN_UNITS = [1024, 512] #@param\n",
    "LEARNING_RATE = 0.1 #@param\n",
    "L1_REGULARIZATION_STRENGTH = 0.0001 #@param\n",
    "L2_REGULARIZATION_STRENGTH = 0.0001 #@param\n",
    "\n",
    "model_dir = tempfile.mkdtemp()\n",
    "single_task_deep_model = tf.estimator.DNNClassifier(\n",
    "    feature_columns=deep_columns,\n",
    "    hidden_units=HIDDEN_UNITS,\n",
    "    optimizer=tf.train.ProximalAdagradOptimizer(\n",
    "      learning_rate=LEARNING_RATE,\n",
    "      l1_regularization_strength=L1_REGULARIZATION_STRENGTH,\n",
    "      l2_regularization_strength=L2_REGULARIZATION_STRENGTH),\n",
    "    model_dir=model_dir)\n",
    "\n",
    "print('Deep neural net model defined.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Tjhqo9XOP2VV"
   },
   "source": [
    "Para que sea más simple, realizaremos el entrenamiento con 1,000 pasos, pero puedes modificar este parámetro."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "UtrhAXwvqtVD"
   },
   "outputs": [],
   "source": [
    "#@title Fit Deep Neural Net Model to the Adult Training Dataset\n",
    "\n",
    "STEPS = 1000 #@param\n",
    "\n",
    "single_task_deep_model.train(\n",
    "    input_fn=csv_to_pandas_input_fn(train_df, num_epochs=None, shuffle=True),\n",
    "    steps=STEPS);\n",
    "\n",
    "print('Deep neural net model is done fitting.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "m0UHu5t-P7G7"
   },
   "source": [
    "Ahora podemos evaluar el rendimiento general del modelo con el conjunto de datos de prueba retenido."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "HDV8hYqvncCy"
   },
   "outputs": [],
   "source": [
    "#@title Evaluate Deep Neural Net Performance\n",
    "\n",
    "results = single_task_deep_model.evaluate(\n",
    "    input_fn=csv_to_pandas_input_fn(test_df, num_epochs=1, shuffle=False),\n",
    "    steps=None)\n",
    "print(\"model directory = %s\" % model_dir)\n",
    "print(\"---- Results ----\")\n",
    "for key in sorted(results):\n",
    "  print(\"%s: %s\" % (key, results[key]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "7j0LrXMGlTDl"
   },
   "source": [
    "Puedes intentar volver a entrenar el modelo con parámetros diferentes. Al final, descubrirás que una red neuronal profunda puede predecir los ingresos de manera aceptable.\n",
    "\n",
    "**Sin embargo, aquí falta la métrica de evaluación para los subgrupos.** En la próxima sección, veremos algunas de las formas en las que puedes evaluar el nivel de subgrupo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "sbwmbnUUU1kY"
   },
   "source": [
    "## Evaluación de equidad con una matriz de confusión\n",
    "\n",
    "Si bien la evaluación del rendimiento general del modelo nos brinda información sobre su calidad, no nos proporciona muchos datos sobre el rendimiento de nuestro modelo en diferentes subgrupos.\n",
    "\n",
    "Cuando evaluamos la equidad del modelo, es importante determinar si los errores de predicción son uniformes en todos los subgrupos o si ciertos subgrupos son más susceptibles a determinados errores de predicción que otros.\n",
    "\n",
    "Una herramienta clave para comparar la prevalencia de los diferentes tipos de errores de modelos es una *matriz de confusión*. Como recordarás del [Módulo de clasificación del Curso intensivo de aprendizaje automático](https://developers.google.com/machine-learning/crash-course/classification/true-false-positive-negative), una matriz de confusión es una cuadrícula que representa las predicciones del modelo en comparación con los datos reales y tabula las estadísticas, para resumir con qué frecuencia la predicción de tu modelo fue correcta, y con qué frecuencia fue incorrecta.\n",
    "\n",
    "Comencemos por crear una matriz de confusión para nuestro modelo de predicción de ingresos, que será binaria porque nuestra etiqueta (`income_bracket`) solo tiene dos valores posibles (`<50K` o `>50K`). Definiremos un ingreso de `>50K` como **etiqueta positiva** y un ingreso de `<50k` como **etiqueta negativa**.\n",
    "\n",
    "**NOTA:** *En este contexto, *positiva* y *negativa* no deben interpretarse como juicios de valor (no estamos sugiriendo que alguien que gana más de USD 50,000 al año es mejor persona que alguien que gana menos de USD 50,000). Se trata solo de términos estándar que se usan para distinguir las dos predicciones posibles que puede hacer el modelo.\n",
    "\n",
    "Los casos en que el modelo hace una predicción correcta (la que coincide con los datos reales) se clasifican como **verdaderos** y los casos en que el modelo realiza una predicción incorrecta se clasifican como **falsos**.\n",
    "\n",
    "Por lo tanto, la matriz de confusión representa cuatro estados posibles:\n",
    "\n",
    "* **verdadero positivo**: El modelo predice `>50K`, y eso coincide con los datos reales.\n",
    "* **verdadero negativo**: El modelo predice `<50K`, y eso coincide con los datos reales.\n",
    "* **falso positivo**: El modelo predice `>50K`, y eso contradice la realidad.\n",
    "* **falso negativo**: El modelo predice `<50K`, y eso contradice la realidad.\n",
    "\n",
    "**NOTA:** Podemos usar la cantidad de resultados de cada uno de esos estados para calcular las métricas de evaluación secundaria, como [precisión y recuperación](https://developers.google.com/machine-learning/crash-course/classification/precision-and-recall)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nsUj_XZHU_mI"
   },
   "source": [
    "### Representación de la matriz de confusión\n",
    "\n",
    "La siguiente celda define una función que usa el módulo `sklearn.metrics.confusion_matrix` para calcular todas las instancias (verdadero positivo, verdadero negativo, falso positivo y falso negativo) que se necesitan para computar nuestra matriz de confusión binaria y nuestras métricas de evaluación."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "CmOWym53VITS"
   },
   "outputs": [],
   "source": [
    "#@test {\"output\": \"ignore\"}\n",
    "#@title Define Function to Compute Binary Confusion Matrix Evaluation Metrics\n",
    "def compute_eval_metrics(references, predictions):\n",
    "  tn, fp, fn, tp = confusion_matrix(references, predictions).ravel()\n",
    "  precision = tp / float(tp + fp)\n",
    "  recall = tp / float(tp + fn)\n",
    "  false_positive_rate = fp / float(fp + tn)\n",
    "  false_omission_rate = fn / float(tn + fn)\n",
    "  return precision, recall, false_positive_rate, false_omission_rate\n",
    "\n",
    "print('Binary confusion matrix and evaluation metrics defined.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ooYe_3lqTCPz"
   },
   "source": [
    "También necesitaremos ayuda para representar la matriz de confusión binaria. La función que se incluye a continuación combina varios módulos de terceros (DataFame de Pandas, Matplotlib, Seaborn) para dibujar la matriz de confusión."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "TMIN_nl1VFuS"
   },
   "outputs": [],
   "source": [
    "#@title Define Function to Visualize Binary Confusion Matrix\n",
    "def plot_confusion_matrix(confusion_matrix, class_names, figsize = (8,6)):\n",
    "    # We're taking our calculated binary confusion matrix that's already in form \n",
    "    # of an array and turning it into a Pandas DataFrame because it's a lot \n",
    "    # easier to work with when visualizing a heat map in Seaborn.\n",
    "    df_cm = pd.DataFrame(\n",
    "        confusion_matrix, index=class_names, columns=class_names, \n",
    "    )\n",
    "    fig = plt.figure(figsize=figsize)\n",
    "    \n",
    "    # Combine the instance (numercial value) with its description\n",
    "    strings = np.asarray([['True Positives', 'False Negatives'],\n",
    "                          ['False Positives', 'True Negatives']])\n",
    "    labels = (np.asarray(\n",
    "        [\"{0:d}\\n{1}\".format(value, string) for string, value in zip(\n",
    "            strings.flatten(), confusion_matrix.flatten())])).reshape(2, 2)\n",
    "\n",
    "    heatmap = sns.heatmap(df_cm, annot=labels, fmt=\"\");\n",
    "    heatmap.yaxis.set_ticklabels(\n",
    "        heatmap.yaxis.get_ticklabels(), rotation=0, ha='right')\n",
    "    heatmap.xaxis.set_ticklabels(\n",
    "        heatmap.xaxis.get_ticklabels(), rotation=45, ha='right')\n",
    "    plt.ylabel('References')\n",
    "    plt.xlabel('Predictions')\n",
    "    return fig\n",
    "\n",
    "print('Binary confusion matrix visualization defined.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hUvBYtwXVzlQ"
   },
   "source": [
    "Ahora que definimos todas las funciones necesarias, podemos calcular la matriz de confusión binaria y las métricas de evaluación con los resultados de [nuestro modelo de red neuronal profunda](#scrollTo=3nYSMg67jWaA). El resultado de esta celda es una vista tabulada que nos permite alternar entre la matriz de confusión y la tabla de las métricas de evaluación."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9enf_Jfi-AVS"
   },
   "source": [
    "### Tarea 4 de FairAware\n",
    "\n",
    "Usa el formulario a continuación para generar matrices de confusión para los dos subgrupos de género: `Female` y `Male`. Compara el número de falsos positivos y falsos negativos de cada subgrupo. ¿Hay alguna disparidad importante en las tasas de error que sugieran que el modelo funciona mejor para un subgrupo que para el otro?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "5TBzaWs1VKTa"
   },
   "outputs": [],
   "source": [
    "#@title Visualize Binary Confusion Matrix and Compute Evaluation Metrics Per Subgroup\n",
    "CATEGORY  =  \"gender\" #@param {type:\"string\"}\n",
    "SUBGROUP =  \"Male\" #@param {type:\"string\"}\n",
    "\n",
    "# Given define subgroup, generate predictions and obtain its corresponding \n",
    "# ground truth.\n",
    "predictions_dict = single_task_deep_model.predict(input_fn=csv_to_pandas_input_fn(\n",
    "    test_df.loc[test_df[CATEGORY] == SUBGROUP], num_epochs=1, shuffle=False))\n",
    "predictions = []\n",
    "for prediction_item, in zip(predictions_dict):\n",
    "    predictions.append(prediction_item['class_ids'][0])\n",
    "actuals = list(\n",
    "    test_df.loc[test_df[CATEGORY] == SUBGROUP]['income_bracket'].apply(\n",
    "        lambda x: '>50K' in x).astype(int))\n",
    "classes = ['Over $50K', 'Less than $50K']\n",
    "\n",
    "# To stay consistent, we have to flip the confusion \n",
    "# matrix around on both axes because sklearn's confusion matrix module by\n",
    "# default is rotated.\n",
    "rotated_confusion_matrix = np.fliplr(confusion_matrix(actuals, predictions))\n",
    "rotated_confusion_matrix = np.flipud(rotated_confusion_matrix)\n",
    "\n",
    "tb = widgets.TabBar(['Confusion Matrix', 'Evaluation Metrics'], location='top')\n",
    "\n",
    "with tb.output_to('Confusion Matrix'):\n",
    "  plot_confusion_matrix(rotated_confusion_matrix, classes);\n",
    "\n",
    "with tb.output_to('Evaluation Metrics'):\n",
    "  grid = widgets.Grid(2,4)\n",
    "\n",
    "  p, r, fpr, fomr = compute_eval_metrics(actuals, predictions)\n",
    "\n",
    "  with grid.output_to(0, 0):\n",
    "    print(' Precision ')\n",
    "  with grid.output_to(1, 0):\n",
    "    print(' %.4f ' % p)\n",
    "\n",
    "  with grid.output_to(0, 1):\n",
    "    print(' Recall ')\n",
    "  with grid.output_to(1, 1):\n",
    "    print(' %.4f ' % r)\n",
    "\n",
    "  with grid.output_to(0, 2):\n",
    "    print(' False Positive Rate ')\n",
    "  with grid.output_to(1, 2):\n",
    "    print(' %.4f ' % fpr)\n",
    "\n",
    "  with grid.output_to(0, 3):\n",
    "    print(' False Omission Rate ')\n",
    "  with grid.output_to(1, 3):\n",
    "    print(' %.4f ' % fomr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "TF3B5h3c-7Fb"
   },
   "source": [
    "### Solución\n",
    "\n",
    "Para ver parte de las estadísticas que descubrimos, haz clic a continuación."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "dhKR49AT_5ZK"
   },
   "source": [
    "Con los parámetros predeterminados, verás que el modelo funciona mejor para \"Male\" (hombre) que para \"Female\" (mujer). Específicamente, en nuestra ejecución, descubrimos que tanto la precisión como la recuperación para los hombres (0.7490 y 0.4795, respectivamente) superaron a las de las mujeres (0.6787 y 0.3716, respectivamente).\n",
    "\n",
    "Esperamos que, después de ver esta demostración de la matriz de confusión, halles que los resultados varían levemente respecto de las métricas generales de rendimiento y entiendas la importancia de evaluar el rendimiento del modelo en cada subgrupo en vez de en todo el conjunto.\n",
    "\n",
    "En tu trabajo, asegúrate de tomar una buena decisión sobre las compensaciones entre falsos positivos, falsos negativos, verdaderos positivos y verdaderos negativos. Por ejemplo, tal vez quieras una tasa de falsos positivos baja, pero una tasa de verdaderos positivos alta. O tal vez quieras precisión alta, pero no te importa tener recuperación baja.  \n",
    "\n",
    "**Elige tus métricas de evaluación en función de las compensaciones que desees.**"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [
    "KlF-lQ8yQ69b",
    "qZ-9vJgSEpHj",
    "TF3B5h3c-7Fb"
   ],
   "name": "intro_to_fairness.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
